Спринт 3/18 → Тема 1/3: Самое необходимое об ООП → Урок 4/8
Принципы ООП
В прошлом уроке вы потренировались объявлять классы в Python. Это отличное достижение, но использования классов мало для того, чтобы сказать, что вы применяете объектно-ориентированный подход при разработке программ. Нужно также понимать принципы, на которых этот подход построен.
У парадигмы объектно-ориентированного программирования четыре принципа:
- абстракция,
- наследование,
- полиморфизм,
- инкапсуляция.
Пока эти слова могут показаться вам страшными и непонятными, но не переживайте, в этом уроке мы расскажем, какой смысл в них скрыт.
Абстракция
Что вы представляете, когда слышите слово «дом»? Скорее всего, первой вашей фантазией будет не совокупность кирпичей, досок и гвоздей, а целое здание с крышей, окнами и дверями.
Абстракция — это использование только значимых характеристик объекта и игнорирование всего остального. Чем меньше характеристик, тем лучше абстракция, но ключевые характеристики убирать нельзя.
Класс в ООП — это абстракция, благодаря которой вы можете объявлять объекты внутри программы, не описывая их подробно.
Для работы с абстракциями используются интерфейсы.
Интерфейс класса — это функциональная часть класса. В ООП интерфейсами называют свойства и методы класса, используя которые можно взаимодействовать с объектом этого класса из любого места в программе.
Описывая класс, разработчик одновременно создаёт интерфейс для обращения к этому классу и к его экземплярам. Для класса
Sword, который вы объявили в прошлом уроке, интерфейсом будут те части класса или объекта, с которыми может взаимодействовать другой код:PYTHON
Абстракция делает процесс моделирования объектов более простым и менее трудоёмким, ведь разработчику не нужно учитывать все свойства объектов.
Наследование
Гибкость объектно-ориентированного программирования проявляется в наследовании. Так называют возможность на основе существующих классов создавать классы-наследники.
Наследование — возможность описать новый класс на базе существующего. При этом дочерние классы могут заимствовать свойства и методы родительского класса.
Благодаря наследованию разработчик организует иерархическую структуру проекта: определяет классу-родителю основные свойства и методы (интерфейс), с помощью которых можно взаимодействовать с объектами любого из дочерних классов.
Например, строителям поставили задачу — построить дома в деревне. Строители понимают, что нужно строить дома с определёнными свойствами — входная дверь, окна, крыша, печь. Также у домов должны быть методы, например, должна быть возможность затопить печь. В мире программиста образ дома, понятный каждому строителю, — это родительский класс.
В деревне можно построить разные дома. Например, одноэтажные землянки для хоббитов или многоэтажные дома, рассчитанные на несколько человеческих семей. Это будут дочерние классы от родительского класса
Дом. У домов будут входные двери, окна и крыши, но у всех разные. Кроме того, у них будет множество других особенностей, например, разная высота потолков и разное количество этажей.Класс
Sword из вашего учебного проекта — это меч. Но меч — не единственное снаряжение, которое может использоваться в играх. Например, есть ещё топоры. У мечей и топоров много общего: у них есть запас прочности, ими можно нанести рубящий удар, а ещё их можно заточить. Такие общие свойства и методы можно вынести в отдельный класс — родительский, его ещё называют базовым.Для примера со снаряжением базовый класс будет выглядеть так:
PYTHON
Для базового класса тоже можно создавать объекты и обращаться к их методам и свойствам.
Родительский класс
MeleeWeapon есть, теперь нужно объявить классы-наследники — Sword (меч) и Axe (топор). Общий синтаксис наследования выглядит следующим образом:PYTHON
Тогда классы
Sword и Axe будут записаны так:PYTHON
Когда вам нужно объявить класс или функцию без логики, используйте ellipsis (в пер. с англ. «многоточие») или ключевое слово
pass. Вы можете выбрать любой вариант, в документации Python и стандарте PEP8 нет ограничений на использование этих конструкций.Такого определения классов будет достаточно для того, чтобы объекты этих классов получили все свойства и методы родительского класса
MeleeWeapon. Код с родительским классом
MeleeWeapon и дочерними Sword и Axe будет выглядеть так: PYTHON
А что с классом
MeleeWeapon? Для него не указан базовый класс, значит у него нет «родителя»? Не значит.В Python все классы напрямую или через классы-родители — наследники встроенного базового класса
object. Все классы в Python могут использовать методы класса object, например знакомый вам метод __str__() определён именно в классе object.Расширяем методы родительского класса
Итак, классы-наследники
Sword и Axe полностью наследуют методы и свойства родительского класса MeleeWeapon. Нужны они для того, чтобы в классы-наследники можно было добавлять свои свойства и расширять возможности методов класса-родителя.Поработаем с классом-наследником
Axe: добавим для него собственное свойство — материал лезвия, а объявление свойства «название оружия» унаследуем из класса-родителя. Чтобы объявить свойство класса-родителя в классе-наследнике, используется функция super(). Тогда код класса Axe будет выглядеть так:PYTHON
Функцию
super() можно также использовать для того, чтобы в дочернем классе вызывать методы из класса-родителя:PYTHON
Теперь при вызове класса-наследника
Axe будет выводиться не только сообщение из класса-родителя «Нанесён рубящий удар…», но и воинственный клич перед этим сообщением — «СОКРУШИТЕЛЬНЫЙ УДАР!».Переопределяем методы родительского класса
Возможности класса-родителя можно не только расширить в классах-наследниках, но и полностью переопределить.
Например, в классе
Sword для метода slashing_blow() можно изменить значение, на которое уменьшится прочность оружия при ударе, и возвращаемое сообщение. При этом метод sharpen() останется полностью унаследованным от класса-родителя и переписывать его не нужно.Переопределим метод
slashing_blow() родительского класса MeleeWeapon в классе-наследнике Sword. Запустите программу и посмотрите на результат:КодPYTHON
У классов
Sword и Axe один родитель, но вывелись разные сообщения и разные показатели прочности оружия. Всё это благодаря тому, что в классе Sword был переопределён один из методов.Собственные методы дочерних классов
Дочерние классы могут использовать не только родительские методы, но и свои.
Например, можно добавить собственный метод для класса
Sword — метод, который будет отвечать за вызов пронзающего удара. Этот метод будет доступен только для объектов класса Sword и объектов-наследников этого класса.PYTHON
Инкапсуляция
Когда вы применяете методы встроенного в интерпретатор класса или класса из стороннего модуля, для вас важно лишь, какие данные в этот метод надо передать, и что метод возвращает. Сама реализация может быть скрыта от вас, и это не помешает вам получить нужный результат. Такое использование методов формирует ещё один принцип ООП.
Инкапсуляция — объединение и скрытие методов и свойств, и предоставление доступа к ним через простой внешний интерфейс.
Даже не имея понятия, как работают встроенные методы
lower, upper или split объекта типа str, вы из документации можете узнать, как с их помощью происходит управление объектами. Методы «инкапсулированы», разработчику предоставлен интерфейс для их вызова: string.upper().А класс
MeleeWeapon инкапсулирует свойства оружия name и strength и его методы slashing_blow() и sharpen(): совершенно необязательно знать, как они работают, можно просто обратиться к ним и получить результат: xiphos.slashing_blow().Полиморфизм
К какому наследнику класса
MeleeWeapon вы ни обратились бы через свойство name или метод slashing_blow() — вы получите ответ (или, как минимум, не столкнётесь с ошибкой), потому что в Python реализован принцип полиморфизма: у всех наследников класса MeleeWeapon есть эти интерфейсы (свойство name и метод slashing_blow()), наследуемые или переопределённые.Полиморфизм — возможность взаимодействовать с объектами разных классов через одинаковые интерфейсы, обращаться к свойствам и методам, общим для всех объектов.
Вы разобрались с принципами ООП, но пока только в теории. Время практики! Переходите к заданиям. А чтобы для решения заданий не приходилось искать нужную теорию по всему курсу, скачивайте шпаргалку.